﻿using System;
using System.Data;
using System.Data.Objects;
using System.Text;
using EntityFrameworkVisualizer.VisualizerObjectSources.CommandFormatters;

namespace EntityFrameworkVisualizer.VisualizerObjectSources
{
    /// <summary>
    /// Visualizer for <see cref="ObjectQuery"/>.
    /// </summary>
    public class ObjectQueryVisualizerObjectSource : QueryVisualizerObjectSource<ObjectQuery>
    {
        /// <summary>
        /// Gets SQL source code.
        /// </summary>
        /// <param name="target">Target object.</param>
        /// <returns>SQL source code.</returns>
        protected override string GetSql(ObjectQuery target)
        {
            if (target == null)
            {
                return null;
            }

            using (var result = target.Execute(MergeOption.NoTracking))
            {
            }

            var stringBuilder = new StringBuilder();

            foreach (var item in target.Parameters)
            {
                stringBuilder.AppendLine(DeclareParameter(item.Name, item.ParameterType, item.Value));
            }

            stringBuilder.AppendLine();
            stringBuilder.AppendLine(target.ToTraceString());

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Format parameter.
        /// </summary>
        /// <param name="parameterName">Parameter name.</param>
        /// <param name="parameterType">Parameter type.</param>
        /// <param name="value">Parameter value.</param>
        /// <returns>String representation.</returns>
        private string DeclareParameter(string parameterName, Type parameterType, object value)
        {
            const string FormatString = "DECLARE @{0} {1} = {2};";

            var resultValue = value;

            var sqlType = GetSqlType(parameterType);
            var typeString = SqlCommandFormatter.GetSqlTypeString(sqlType);
            var sqlValue = SqlCommandFormatter.FormatValue(sqlType, value);

            return string.Format(FormatString, parameterName, typeString, sqlValue);
        }

        /// <summary>
        /// Returns <see cref="SqlDbType"/> from <see cref="Type"/>.
        /// </summary>
        /// <param name="type"><see cref="Type"/>.</param>
        /// <returns><see cref="SqlDbType"/>.</returns>
        private SqlDbType GetSqlType(Type type)
        {
            SqlDbType result = SqlDbType.Int;

            if (type == typeof(string))
            {
                result = SqlDbType.NVarChar;
            }
            else if (type == typeof(bool))
            {
                result = SqlDbType.Bit;
            }
            else if (type == typeof(int))
            {
                result = SqlDbType.Int;
            }
            else if (type == typeof(long))
            {
                result = SqlDbType.BigInt;
            }
            else if (type == typeof(short))
            {
                result = SqlDbType.SmallInt;
            }
            else if (type == typeof(float))
            {
                result = SqlDbType.Real;
            }
            else if (type == typeof(decimal))
            {
                result = SqlDbType.Decimal;
            }
            else if (type == typeof(double))
            {
                result = SqlDbType.Float;
            }
            else if (type == typeof(byte[]))
            {
                result = SqlDbType.VarBinary;
            }
            else if (type == typeof(Guid))
            {
                result = SqlDbType.UniqueIdentifier;
            }
            else if (type == typeof(byte))
            {
                result = SqlDbType.TinyInt;
            }
            else if (type == typeof(DateTime))
            {
                result = SqlDbType.DateTime2;
            }

            return result;
        }
    }
}
